use std::collections::HashMap;
use std::env;
use std::process::{Command, Stdio};

use anyhow::{Result};
use toml::Value;

use crate::log;

pub struct CmdUtil {
    pub current_dir: String,
    pub envs: HashMap<String, String>,
    pub debug: bool,
}

impl CmdUtil {
    pub fn new(current_dir: String, envs: HashMap<String, Value>, execution: HashMap<String, Value>) -> Result<Self> {
        let run_envs = Self::get_env(envs, execution)?;
        let debug = match env::var_os("RT_DEBUG") {
            Some(val) => {
                if val.eq("0") {
                    false
                } else {
                    true
                }
            }
            None => false
        };
        Ok(Self { current_dir, envs: run_envs, debug })
    }

    fn get_env(envs: HashMap<String, Value>, execution: HashMap<String, Value>) -> Result<HashMap<String, String>> {
        let mut run_envs = HashMap::new();
        let mut path_value = match env::var_os("PATH") {
            Some(v) => v.to_str().unwrap().to_string(),
            None => "".to_string()
        };
        for (_, val) in execution.iter() {
            let value = if val.is_str() { val.as_str().unwrap_or("").to_string() } else { val.to_string() };
            // run_envs.insert(key.to_string(), value.clone());
            path_value = if path_value.is_empty() { value.clone() } else { [path_value, value.clone()].join(":") };
        }
        run_envs.insert("PATH".to_string(), path_value);
        for (key, val) in envs.iter() {
            let value = if val.is_str() { val.as_str().unwrap().to_string() } else { val.to_string() };
            run_envs.insert(format!("{}", key), value);
        }
        Ok(run_envs)
    }

    pub fn add_env(&mut self, key: String, value: String) -> Result<()> {
        self.envs.insert(key, value);
        Ok(())
    }
    pub fn set_debug(&mut self, debug: bool) {
        self.debug = debug;
    }

    fn set_env(&mut self, command: &mut Command) -> Result<()> {
        command.env_clear();
        if self.debug {
            log("环境变量：".to_string());
            self.envs.iter().for_each(|(key, value)| log(format!("{} = {}", key, value)));
        }
        command.envs(self.envs.clone());
        Ok(())
    }

    pub fn set_cmd_encode() -> Result<()> {
        if cfg!(target_os = "windows") {
            Command::new("cmd.exe").arg("/C").arg("chcp")
                .arg("65001").stdout(Stdio::null())
                .stderr(Stdio::null()).output()?;
        }
        Ok(())
    }

    pub fn exec(&mut self, cmd: &str) -> Result<()> {
        let mut command;
        if cfg!(target_os = "windows") {
            command = Command::new("cmd.exe");
            command.arg("/C");
        } else {
            command = Command::new("sh");
            command.arg("-c");
        }
        command.stdout(Stdio::inherit()).stderr(Stdio::inherit()).arg(cmd);
        self.set_env(&mut command)?;
        if !self.current_dir.is_empty() {
            command.current_dir(&self.current_dir);
        }
        if self.debug {
            log(format!("执行命令：{}", cmd));
        }
        let mut spawn = command.spawn()?;
        drop(command);
        spawn.wait()?.code().unwrap_or(1);
        Ok(())
    }

    pub fn exec_cmds(&mut self, cmds: Vec<String>) -> Result<()> {
        for cmd in cmds {
            self.exec(&cmd)?;
        }
        Ok(())
    }
}
